#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/time.h>
using namespace std;

//ICMP校验和计算
unsigned short checksum(void *b, int len)
{
    unsigned short *buf = (unsigned short* )b;
    unsigned int sum = 0;
    unsigned short result;

    for (sum = 0; len > 1; len -= 2)
    {
	    sum += *buf++;
	}
    if (len == 1) sum += *(unsigned char* )buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    result = ~sum;
    return result;
}

int main()
{
    int sockfd;
    char buffer[1024];
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);

    //创建原始套接字
    if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{
        perror("socket");
        return 1;
    }

    cout << "PING服务器已启动，等待请求...\n";

    while (true)
	{
        //接收ICMP包
        int bytes_received = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr* )&client_addr, &addr_len);
        if (bytes_received < 0)
		{
            perror("recvfrom");
            continue;
        }

        //解析IP头
        struct iphdr *ip_header = (struct iphdr* )buffer;
        int ip_header_len = ip_header->ihl * 4;

        //解析ICMP头
        struct icmphdr *icmp_header = (struct icmphdr *)(buffer + ip_header_len);

        //只处理Echo请求(Type=8)
        if (icmp_header->type == ICMP_ECHO)
		{
            char src_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &(client_addr.sin_addr), src_ip, INET_ADDRSTRLEN);
            cout << "收到来自 " << src_ip << " 的PING请求 (ID="  << ntohs(icmp_header->un.echo.id)  << ", Seq=" << ntohs(icmp_header->un.echo.sequence) << ")\n";

            //构造回复包
            struct icmphdr reply_header;
            reply_header.type = ICMP_ECHOREPLY;//Echo回复(Type=0)
            reply_header.code = 0;
            reply_header.un.echo.id = icmp_header->un.echo.id;
            reply_header.un.echo.sequence = icmp_header->un.echo.sequence;
            reply_header.checksum = 0;
            reply_header.checksum = checksum(&reply_header, sizeof(reply_header));

            //发送回复
            if (sendto(sockfd, &reply_header, sizeof(reply_header), 0, (struct sockaddr *)&client_addr, addr_len) <= 0) perror("sendto");
        }
    }

    close(sockfd);
    return 0;
}